iT邦幫忙

2022 iThome 鐵人賽

DAY 21
0
Mobile Development

Android Studio 30天學習紀錄系列 第 21

Android Studio 30天學習紀錄-Day 21 Room

  • 分享至 

  • xImage
  •  

Room是一個本地資料庫與SQLiteDataBase相同,他一樣也提供了CRUD的一些註解做使用,在應用程式做清除資料的時候資料也會隨之而消失,那麼就開始今日的主題,首先需要在gradle中加入一些依賴:

依賴

plugins{
//...
    id 'kotlin-kapt'
    id 'kotlin-android-extensions'
}
android{
//...
}
dependcies{
//room
    implementation"androidx.room:room-runtime:2.4.3"
    annotationProcessor"androidx.room:room-compiler:2.4.3"
    androidTestImplementation "androidx.room:room-testing:2.4.3"
    implementation "androidx.lifecycle:lifecycle-extensions:2.2.0"
    annotationProcessor "androidx.lifecycle:lifecycle-compiler:2.3.1"
    kapt "androidx.lifecycle:lifecycle-compiler:2.3.0-alpha07"
    kapt "androidx.room:room-compiler:2.3.0-alpha02"
}

UI(activity_main)

那麼首先先附上今日UI:

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/recyclerview"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        app:layout_constraintBottom_toTopOf="@+id/guideline2"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <EditText
        android:id="@+id/et_account"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:ems="10"
        android:hint="Account"
        android:inputType="textPersonName"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.497"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintVertical_bias="0.59" />

    <EditText
        android:id="@+id/et_password"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:ems="10"
        android:hint="Password"
        android:inputType="textPersonName"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.497"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintVertical_bias="0.704" />

    <Button
        android:id="@+id/btn_send"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="新增"
        android:textSize="24dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.779"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintVertical_bias="0.853" />

    <androidx.constraintlayout.widget.Guideline
        android:id="@+id/guideline2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        app:layout_constraintGuide_percent="0.35" />

    <TextView
        android:id="@+id/txv_show"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textSize="30sp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintVertical_bias="0.468" />

    <Button
        android:id="@+id/btn_update"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="修改"
        android:textSize="24dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.242"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintVertical_bias="0.853" />

</androidx.constraintlayout.widget.ConstraintLayout>

RecycleView

UI(item_data)

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">

    <TextView
        android:id="@+id/itemId"
        android:layout_width="70dp"
        android:layout_height="match_parent"
        android:textSize="28dp"
        android:text="" />

    <TextView
        android:id="@+id/itemText"
        android:layout_width="300dp"
        android:layout_height="match_parent"
        android:textSize="28dp"
        android:text="" />

    <ImageButton
        android:id="@+id/btn_delete"
        android:layout_width="40dp"
        android:layout_height="match_parent"
        android:layout_gravity="center"
        android:background="#00FF0000"
        app:srcCompat="@android:drawable/ic_menu_delete" />

</LinearLayout>

Adapter(DataAdapter)

class DataAdapter(val activity: MainActivity , dataList:MutableList<DataModel>) : RecyclerView.Adapter<DataAdapter.ViewHolder>() {

    var arrayList:MutableList<DataModel> = dataList
    var onItemClick:OnItemClickListener?=null
    var onItemDelete:OnItemDeleteListener?=null

    class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
        val itemId = itemView.findViewById<TextView>(R.id.itemId)
        val itemText = itemView.findViewById<TextView>(R.id.itemText)
        val btn_delete = itemView.findViewById<ImageButton>(R.id.btn_delete)
    }
    //刪除
    fun deleteData(position: Int){
        Thread {
            Log.d("DeleteTag","pos:"+position)
            DataBase.getInstance(activity)!!.userDao().delete(arrayList[position])

        }.start()
        notifyItemRemoved(position)
        arrayList.remove(arrayList[position])
        notifyDataSetChanged()
    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
        return ViewHolder(
            LayoutInflater.from(parent.context)
            .inflate(R.layout.item_data,null))
    }

    override fun getItemCount(): Int {
        return arrayList.size
    }

    override fun onBindViewHolder(holder: ViewHolder, position: Int) {
        holder.btn_delete.setOnClickListener {
            deleteData(position)
            onItemDelete?.onItemDelete(position)
        }
        holder.itemId.setText(""+(position+1))
        holder.itemText.setText(""+arrayList.get(position).account)
        //view點擊事件
        holder.itemView.setOnClickListener{
            Log.d("click",""+onItemClick)
            onItemClick?.onItemClick(arrayList[position],position)
        }
    }
    interface OnItemClickListener {
        fun onItemClick(myData: DataModel,position:Int)
    }
    interface OnItemDeleteListener{
        fun onItemDelete(position:Int)
    }
}

Room

Room主要可由三大部分組成:

  • Entity
    標註@Entity、為data class,主要用於定義資料有哪些,並且給他一個tableName。
@Entity(tableName="user")
data class DataModel(
    @PrimaryKey(autoGenerate = true)
    var _id: Int,
    @ColumnInfo(name = "Account")
    var account :String,
    @ColumnInfo(name = "Password")
    var password:String
)
  • Dao
    標註@Dao、為Interface,主要用於對資料表進行CRUD與Query的處理,註解除了查其他都有。
@Dao
interface Dao {
    //新增資料(當發生衝突則覆蓋)
    @Insert(onConflict = OnConflictStrategy.REPLACE)
    fun insertUser(data: DataModel)
    //取得user資料表的所有資料
    @Query("SELECT * FROM user")
    fun getAll(): MutableList<DataModel>
    //更新資料
    @Update
    fun updateUser(data: DataModel)
    //刪除資料
    @Delete
    fun delete(data: DataModel)
}
  • Database
    標註@Database、為abstract class,使其繼承RoomDatabase,主要用於建立資料庫。
@Database(entities = [DataModel::class], version = 1)
abstract class DataBase : RoomDatabase() {
    abstract fun userDao(): Dao
    companion object {
        private var instance: DataBase? = null
        fun getInstance(context: Context): DataBase? {
            if (instance == null) {
                instance = create(context) //新增資料庫
            }
            return instance
        }

        private fun create(context: Context): DataBase{
            return Room.databaseBuilder(context, DataBase::class.java, "Application.db").build()
        }
    }
}

MainActivity

需要注意的是,當執行資料庫的方法處理時,需要開另一個執行緒或協程去執行,不然會報錯。
那麼接著最後來設計需要的activity:

class MainActivity : AppCompatActivity(){
    //資料List
    lateinit var users: MutableList<DataModel>
    //RecyclerView調配器
    var dataAdapter:DataAdapter? = null
    //執行crud、query語法
    var userDao : Dao ?= null
    //RecyclerView的position
    var cyclePos :Int = -1
    //資料的id
    var pos :Int = -1
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        Thread{
            //資料庫建立
            val db = DataBase.getInstance(applicationContext)
            if (db != null) {
                userDao = db.userDao()
                //更新清單顯示
                setRecyclerView()
            }
        }.start()
        //新增按鈕
        btn_send.setOnClickListener { view->
            if (!et_account.text.toString().equals("")&&!et_password.text.toString().equals("")){
                Log.d("TAGGGG","SEND!"+userDao)
                txv_show.setText("新增1筆資料")
                Thread {
                    //新增資料
                    userDao?.insertUser(DataModel(0,et_account.text.toString(),et_password.text.toString()))
                    //更新清單顯示
                    setRecyclerView()
                    //輸入框清空
                    runOnUiThread {
                        et_account.setText("")
                        et_password.setText("")
                    }
                }.start()
                //將點擊項目的position設-1
                pos=-1
            }
        }
        //更新按鈕
        btn_update.setOnClickListener { view->
            if (!et_account.text.toString().equals("")&&!et_password.text.toString().equals("")) {
                Log.d("TAGGGG","UPDATE!"+userDao)
                txv_show.setText("已更新第"+(cyclePos+1)+"筆")
                Thread {
                    //更新資料
                    userDao?.updateUser(DataModel(pos,et_account.text.toString(),et_password.text.toString()))
                    //更新清單顯示
                    setRecyclerView()
                    runOnUiThread {
                        //輸入框清空
                        et_account.setText("")
                        et_password.setText("")
                    }
                }.start()
            }
        }
    }
    //更新清單顯示、項目點擊、刪除按紐處理
    fun setRecyclerView(){
        users = userDao!!.getAll()
        runOnUiThread {
            dataAdapter = DataAdapter(MainActivity(),users)
            recyclerview.setLayoutManager(LinearLayoutManager(this)) //使用LinearLayout布局
            recyclerview.adapter=dataAdapter
            //項目點擊事件
            dataAdapter?.onItemClick = object :DataAdapter.OnItemClickListener{
                override fun onItemClick(myData: DataModel,position:Int) {
                    //資料的id
                    pos=myData._id
                    //list的position
                    cyclePos=position
                    txv_show.setText("選取第"+(cyclePos+1)+"筆")
                    et_account.setText(""+myData.account)
                    et_password.setText(""+myData.password)
                }
            }
            //刪除按紐事件
            dataAdapter?.onItemDelete = object :DataAdapter.OnItemDeleteListener {
                override fun onItemDelete(position: Int) {
                    txv_show.setText("已刪除第"+(position+1)+"筆")
                }

            }
        }
    }
}

成果

https://ithelp.ithome.com.tw/upload/images/20221002/20139259n3dNUuZUsQ.jpg
https://ithelp.ithome.com.tw/upload/images/20221002/20139259qJUuHy8oEU.jpg
https://ithelp.ithome.com.tw/upload/images/20221002/20139259W0haxzrh9e.jpg
https://ithelp.ithome.com.tw/upload/images/20221002/201392597q0pQx9Bf3.jpg


上一篇
Android Studio 30天學習紀錄-Day 20 MvvM
下一篇
Android Studio 30天學習紀錄-Day22 Bluetooth掃描&配對
系列文
Android Studio 30天學習紀錄30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言